#include "minizib.h"
#include "zlib.h"
#include "zip.h"

#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__))
        #ifndef __USE_FILE_OFFSET64
                #define __USE_FILE_OFFSET64
        #endif
        #ifndef __USE_LARGEFILE64
                #define __USE_LARGEFILE64
        #endif
        #ifndef _LARGEFILE64_SOURCE
                #define _LARGEFILE64_SOURCE
        #endif
        #ifndef _FILE_OFFSET_BIT
                #define _FILE_OFFSET_BIT 64
        #endif
#endif

#ifdef __APPLE__
// In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions
#define FOPEN_FUNC(filename, mode) fopen(filename, mode)
#define FTELLO_FUNC(stream) ftello(stream)
#define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin)
#else
#define FOPEN_FUNC(filename, mode) fopen64(filename, mode)
#define FTELLO_FUNC(stream) ftello64(stream)
#define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin)
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>

#ifdef _WIN32
# include <direct.h>
# include <io.h>
#define USEWIN32IOAPI
#include "iowin32.h"
#else
# include <unistd.h>
# include <utime.h>
# include <sys/types.h>
# include <sys/stat.h>
#endif

#define WRITEBUFFERSIZE (16384)
#define MAXFILENAME (256)

#ifdef _WIN32
uLong filetime(const char *f, tm_zip *tmzip, uLong *dt)
{
  int ret = 0;
  {
      FILETIME ftLocal;
      HANDLE hFind;
      WIN32_FIND_DATAA ff32;

      hFind = FindFirstFileA(f,&ff32);
      if (hFind != INVALID_HANDLE_VALUE)
      {
        FileTimeToLocalFileTime(&(ff32.ftLastWriteTime),&ftLocal);
        FileTimeToDosDateTime(&ftLocal,((LPWORD)dt)+1,((LPWORD)dt)+0);
        FindClose(hFind);
        ret = 1;
      }
  }
  return ret;
}
#else
#ifdef __APPLE__
uLong filetime(const char *f, tm_zip *tmzip, uLong *dt)
{
  int ret=0;
  struct stat s;        /* results of stat() */
  struct tm* filedate;
  time_t tm_t=0;

  if (strcmp(f,"-")!=0)
  {
    char name[MAXFILENAME+1];
    int len = strlen(f);
    if (len > MAXFILENAME)
      len = MAXFILENAME;

    strncpy(name, f,MAXFILENAME-1);
    /* strncpy doesnt append the trailing NULL, of the string is too long. */
    name[ MAXFILENAME ] = '\0';

    if (name[len - 1] == '/')
      name[len - 1] = '\0';
    /* not all systems allow stat'ing a file with / appended */
    if (stat(name,&s)==0)
    {
      tm_t = s.st_mtime;
      ret = 1;
    }
  }
  filedate = localtime(&tm_t);

  tmzip->tm_sec  = filedate->tm_sec;
  tmzip->tm_min  = filedate->tm_min;
  tmzip->tm_hour = filedate->tm_hour;
  tmzip->tm_mday = filedate->tm_mday;
  tmzip->tm_mon  = filedate->tm_mon ;
  tmzip->tm_year = filedate->tm_year;

  return ret;
}
#else
uLong filetime(char *f, tm_zip *tmzip, uLong *dt)
{
    return 0;
}
#endif
#endif


int isLargeFile(const char* filename)
{
    int largeFile = 0;
    ZPOS64_T pos = 0;
    FILE* pFile = FOPEN_FUNC(filename, "rb");

    if(pFile != NULL)
    {
        FSEEKO_FUNC(pFile, 0, SEEK_END);
        pos = FTELLO_FUNC(pFile);
        printf("File : %s is %lld bytes\n", filename, pos);
        if(pos >= 0xffffffff)
         largeFile = 1;
        fclose(pFile);
    }

    return largeFile;
}

int Comparess(const QString& saveFile, const QStringList& files)
{
    int i;
    int opt_compress_level=Z_DEFAULT_COMPRESSION;
    int err=0;
    int size_buf=WRITEBUFFERSIZE;
    void* buf=NULL;

    buf = (void*)malloc(size_buf);
    if (buf==NULL)
    {
        return ZIP_INTERNALERROR;
    }
    zipFile zf;
    int errclose;
#   ifdef USEWIN32IOAPI
    zlib_filefunc64_def ffunc;
    fill_win32_filefunc64A(&ffunc);
    zf = zipOpen2_64(saveFile.toStdString().c_str(), 0,NULL,&ffunc);
#   else
    zf = zipOpen64(saveFile.toStdString().c_str(), 0);
#endif

    if (zf == NULL)
    {
        qDebug() << QString("error opening %1\n").arg(saveFile);
        err= ZIP_ERRNO;
    }
    else
        qDebug() << QString("creating %1\n").arg(saveFile);

    for (i=0; i< files.size();i++)
    {
        FILE * fin = nullptr;
        int size_read;
        std::string temp = files[i].toStdString();
        const char* filenameinzip = temp.c_str();
        const char *savefilenameinzip;
        zip_fileinfo zi;
        unsigned long crcFile=0;
        int zip64 = 0;

        zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour =
        zi.tmz_date.tm_mday = zi.tmz_date.tm_mon = zi.tmz_date.tm_year = 0;
        zi.dosDate = 0;
        zi.internal_fa = 0;
        zi.external_fa = 0;
        filetime(filenameinzip,&zi.tmz_date,&zi.dosDate);
        zip64 = isLargeFile(filenameinzip);

        /* The path name saved, should not include a leading slash. */
        /*if it did, windows/xp and dynazip couldn't read the zip file. */
        savefilenameinzip = filenameinzip;
        while( savefilenameinzip[0] == '\\' || savefilenameinzip[0] == '/' )
        {
            savefilenameinzip++;
        }

        /**/
        err = zipOpenNewFileInZip3_64(zf,savefilenameinzip,&zi,
            NULL,0,NULL,0,NULL /* comment*/,
            (opt_compress_level != 0) ? Z_DEFLATED : 0,
            opt_compress_level,0,
            /* -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, */
            -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
            NULL,crcFile, zip64);

        if (err != ZIP_OK)
            qDebug() << QString("error in opening  %1 in zipfile\n").arg(filenameinzip);
        else
        {
            fin = FOPEN_FUNC(filenameinzip,"rb");
            if (fin==NULL)
            {
                err=ZIP_ERRNO;
                qDebug() << QString("error in opening  %1 reading\n").arg(filenameinzip);
            }
        }

        if (err == ZIP_OK)
        do
        {
           err = ZIP_OK;
           size_read = (int)fread(buf,1,size_buf,fin);
           if (size_read < size_buf)
                if (feof(fin)==0)
                {
                    qDebug() << QString("error in reading").arg(filenameinzip);
                    err = ZIP_ERRNO;
                }

           if (size_read>0)
           {
               err = zipWriteInFileInZip (zf,buf,size_read);
               if (err<0)
               {
                   qDebug() << QString("error in writing %1 in the zipfile\n").arg(filenameinzip);
               }

           }
        } while ((err == ZIP_OK) && (size_read>0));

       if (fin)
           fclose(fin);

       if (err<0)
           err=ZIP_ERRNO;
       else
       {
           err = zipCloseFileInZip(zf);
           if (err!=ZIP_OK)
               qDebug() << QString("error in closing %1 in the zipfile error\n").arg(filenameinzip);
       }
    }

    errclose = zipClose(zf, NULL);
    if (errclose != ZIP_OK)
        qDebug() << QString("error in closing %1\n").arg(saveFile);

    return err;
}
